﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;

namespace ClownStopper
{
    public interface IMessageCreator
    {
        int ListenPort { get; set; }

        int ServerListenPort { get; set; }

        String Server { get; set; }

        String Group { get; set; }


        event OnReceiveMessageDelegate OnReceiveMessage;

        void BroadcastMessage(IMessage message, bool isAsync);
        void SendMessage(IMessage message, IMessageClientDetails details, bool isAsync);
        void SendMessage(IMessage message, String name, bool isAsync);

        void Close();
    }

    public class MessageCreator : IMessageCreator
    {
        private readonly IMessageClient mClient;
        private readonly Dictionary<IMessageClientDetails, IMessageClient> mClientConnections;
        private readonly Collection<IMessageClientDetails> mGroupClients;
        private readonly IMessageServer mListener;

        private event OnReceiveMessageDelegate mOnReceiveMessage;

        private void OnReceivePeerMessage(object sender, ReceiveMessageEventArgs e)
        {
            if (mOnReceiveMessage != null)
                mOnReceiveMessage.Invoke(sender, e);
        }


        public MessageCreator(int listenPort, int serverListenPort, string server, string group)
            : this(listenPort, serverListenPort, server, group, null)
        {
        }


        public MessageCreator(int listenPort, int serverListenPort, string server, string group, IMessageServer messageServer)
        {
            ListenPort = listenPort;
            ServerListenPort = serverListenPort;
            Server = server;
            Group = group;

            mGroupClients = new Collection<IMessageClientDetails>();
            //////////////////////////////////////////////////////////////////////////
            if (messageServer == null)
            {
                mListener = new MessageServer(serverListenPort, group);
                //mListener.ListenPort = listenPort;
                ((MessageServer)mListener).Group = group;
                ((MessageServer)mListener).GroupClientsDetails = mGroupClients;
                ((MessageServer)mListener).OnReceiveMessage += OnReceivePeerMessage;
            }
            else
            {
                mListener = messageServer;
            }


            //////////////////////////////////////////////////////////////////////////
            mClient = new MessageClient(listenPort, server, serverListenPort, group);
            mClient.Initialize();
            //////////////////////////////////////////////////////////////////////////
            mClientConnections = new Dictionary<IMessageClientDetails, IMessageClient>();
        }

        public Collection<IMessageClientDetails> GroupClients
        {
            get
            {
                if (mListener != null)
                    return ((MessageServer)mListener).GroupClientsDetails;
                return null;
            }
        }

        #region IMessageCreator Members

        public event OnReceiveMessageDelegate OnReceiveMessage
        {
            add { mOnReceiveMessage += value; }
            remove { mOnReceiveMessage -= value; }
        }

        public int ListenPort { get; set; }

        public String Server { get; set; }

        public int ServerListenPort { get; set; }

        public String Group { get; set; }

        public void Close()
        {
            var msg = new UnregisteredClient { Group = Group, Client = new MessageClientDetails { ClientIPAddress = ((MessageServer)mListener).LocalIPAddress, ClientListenPort = ListenPort } };
            mClient.SendMessageAsync(msg);
            BroadcastMessage(msg, true);
            Thread.Sleep(100); //Sleep required due to the use of asynchronous methods
        }

        public void SendMessage(IMessage message, IMessageClientDetails details, bool isAsync)
        {
            if (message == null)
            {
                //throw a null reference exception
                throw new NullReferenceException("The supplied IMessage object is null!");
            }

            if (details == null)
            {
                //throw a null reference exception
                throw new NullReferenceException("The supplied ICollaborativeClientDetails object is null!");
            }

            IMessageClient currClient = null;
            if (mClientConnections.ContainsKey(details))
            {
                currClient = mClientConnections[details];
            }
            else
            {
                currClient = new MessageClient(ListenPort, details.ClientIPAddress, details.ClientListenPort, Group);
                currClient.Initialize();
            }
            if (isAsync)
            {
                currClient.SendMessageAsync(message);
            }
            else
            {
                currClient.SendMessage(message);
            }

            if (!mClientConnections.ContainsKey(details))
            {
                mClientConnections[details] = currClient;
            }
        }

        public void SendMessage(IMessage message, String name, bool isAsync)
        {
            if (name == null)
            {
                //throw a null reference exception
                throw new NullReferenceException("The supplied name is null!");
            }

            if (name.Length == 0)
            {
                //throw an exception
                throw new Exception("The supplied name is invalid!");
            }

            IMessageClientDetails details = null;

            if (GroupClients == null)
            {
                throw new NullReferenceException("The clients list is null!");
            }

            if (GroupClients.Count == 0)
            {
                throw new Exception("There are no clients available!");
            }

            for (int i = 0; i < GroupClients.Count; ++i)
            {
                if (GroupClients[i].ClientName == name)
                {
                    details = GroupClients[i];
                    break;
                }
            }

            SendMessage(message, details, isAsync);
        }

        public void BroadcastMessage(IMessage message, bool isAsync)
        {
            for (int i = 0; i < ((MessageServer)mListener).GroupClientsDetails.Count; ++i)
            {
                IMessageClientDetails currClientDetails = ((MessageServer)mListener).GroupClientsDetails[i];
                IMessageClient currClient = null;
                if (mClientConnections.ContainsKey(currClientDetails))
                {
                    currClient = mClientConnections[currClientDetails];
                }
                else
                {
                    currClient = new MessageClient(currClientDetails.ClientListenPort, Server, ServerListenPort, Group);
                    currClient.Initialize();
                }
                if (isAsync)
                {
                    currClient.SendMessageAsync(message);
                }
                else
                {
                    currClient.SendMessage(message);
                }

                if (!mClientConnections.ContainsKey(currClientDetails))
                {
                    mClientConnections[currClientDetails] = currClient;
                }
            }
        }

        #endregion


    }
}